import math
import random


# 加载 Iris 数据集
def load_iris_data(filename='iris.data'):
    """
    从指定文件加载Iris数据集，每一行代表一个样本，包含特征值和类别标签。

    参数:
        filename (str): 数据文件的路径，默认为'iris.data'

    返回:
        list: 包含元组的列表，每个元组的第一个元素是特征列表，第二个元素是类别标签字符串。
    """
    data = []
    with open(filename, 'r') as file:
        for line in file:
            stripped_line = line.strip()
            if stripped_line:
                # 分割行数据，最后一个元素作为类别标签，其余转换为浮点数作为特征
                row = stripped_line.split(',')
                features = list(map(float, row[:-1]))
                label = row[-1]
                data.append((features, label))
    return data


# 计算两个点之间的欧氏距离
def euclidean_distance(point1, point2):
    """
    计算两个多维空间点之间的欧氏距离。

    参数:
        point1 (list of float): 第一个点的坐标列表
        point2 (list of float): 第二个点的坐标列表

    返回:
        float: 两点之间的欧氏距离
    """
    return math.sqrt(sum((x - y) ** 2 for x, y in zip(point1, point2)))


# KNN 分类算法
def knn_classify(train_data, test_point, k):
    """
    使用KNN算法对单个测试点进行分类。

    参数:
        train_data (list of tuple): 训练数据集，每个元组包含特征列表和类别标签
        test_point (list of float): 测试点的特征列表
        k (int): 考虑最近邻的数量

    返回:
        str: 预测的类别标签
    """
    distances = [(euclidean_distance(features, test_point), label) for features, label in train_data]
    distances.sort(key=lambda x: x[0])  # 按距离从小到大排序
    nearest_neighbors = distances[:k]  # 取前k个最近邻
    labels = [label for _, label in nearest_neighbors]
    # 多数表决决定最终类别
    majority_vote = max(set(labels), key=labels.count)
    return majority_vote


# 划分训练集和测试集
def train_test_split(data, test_size=0.2, seed=None):
    """
    将数据集划分为训练集和测试集。

    参数:
        data (list): 原始数据集
        test_size (float): 测试集所占比例，默认为0.2
        seed (int or None): 随机种子，用于保证结果的可重复性

    返回:
        tuple: 包含两个列表的元组，第一个是训练集，第二个是测试集
    """
    if seed is not None:
        random.seed(seed)  # 设置随机种子以保证结果可重复
    random.shuffle(data)
    split_index = int(len(data) * (1 - test_size))
    return data[:split_index], data[split_index:]


# 主函数
if __name__ == "__main__":
    # 加载数据
    print("正在加载Iris数据集...")
    data = load_iris_data()

    # 划分训练集和测试集，设置随机种子以保证结果的可重复性
    train_data, test_data = train_test_split(data, test_size=0.2, seed=42)

    # 设置KNN算法中的k值
    k = 3  # 最近邻数量
    correct_predictions = 0
    total_tests = len(test_data)

    print(f"使用KNN算法（k={k}）对测试集进行分类...")
    for features, true_label in test_data:
        predicted_label = knn_classify(train_data, features, k)
        if predicted_label == true_label:
            correct_predictions += 1

    accuracy = correct_predictions / total_tests
    print(f"KNN 分类准确率: {accuracy * 100:.2f}%")